<?php

require_once("ISAdministradorPersistencia.php");

/**
 * El Administrador de persistencia se encarga de administrar y centralizar
 * la ejecucion de operaciones en Base de Datos por medio de los DAOs.
 * Ofrece servicios de transaccionalidad para las operaciones y su comportamiento
 * es polimorfico. Se hace uso de PDO (PHP Data Objects) como API que nos permite
 * abstraernos de muchas operaciones de bajo nivel y nos permite independencia del RDBMS
 *
 */

class SAdministradorPersistencia implements ISAdministradorPersistencia {
	
	private $conexionDB;
	private $tipoOperacion;
	private $sdn;
	private $usuario;
	private $password;
	private $ultimoID;	
	
	public function __construct($motor, $host, $db, $usuario, $password){
		
		$sdn;
		switch ($motor){
			case 'mysql':
				$this->sdn = 'mysql:host='.$host.';dbname='.$db.'';
			break;
			
			case 'oracle':
				$this->sdn = 'oci:dbname=//'.$host.':1521/'.$db.'';
			break;
			
			case 'mssql':
				
			break;
			
			case 'pgsql':
				$this->sdn = 'pgsql:host=$host port=5432 dbname=$db';
			break;
		}
		$this->usuario = $usuario;
		$this->password = $password;
		$this->abrirSesion();
		//echo $this->sdn;
		//$this->ultimoID = -1;
	}
	
	/**
	 * Ejecuta una consulta(SELECT) en la base de datos, la operacion que se ejecuta
	 * depende del DAO que se le pase como parametro 
	 *
	 * @param ISDAO $unDAO
	 * @return unknown
	 */
    public function consultar(ISDAO $unDAO){
        $this->tipoOperacion = ISAdministradorPersistencia::OPERACION_CONSULTA;
        $unDAO->setAdministradorPersistencia($this);
        
        $unDAO->limpiarSentencias();
        $unDAO->consultar();
        
        $unosResultados = $this->ejecutarSentencia($unDAO->getSentenciasSQL());
        $resultados = $unDAO->procesarResultados($unosResultados);
        //$this->columnas = $unDAO->getColumnas();
        return $resultados;
    }
    
    /**
     * Ejecuta una eliminacion(DELETE) unitaria en la base de datos, la operacion que se ejecuta
	 * depende del DAO que se le pase como parametro
     *
     * @param ISDAO $unDAO
     * @return unknown
     */
    public function eliminar(ISDAO $unDAO){
        $this->tipoOperacion = ISAdministradorPersistencia::OPERACION_ELIMINACION;
        $unDAO->setAdministradorPersistencia($this);

        $unDAO->limpiarSentencias();
        $unDAO->eliminar();
        
        $unosResultados = $this->ejecutarSentencia($unDAO->getSentenciasSQL());
        return $unosResultados;
    }
    
    /**
     * Ejecuta una insercion(INSERT) unitaria en la base de datos, la operacion que se ejecuta
	 * depende del DAO que se le pase como parametro
     *
     * @param ISDAO $unDAO
     * @return unknown
     */
    public function crear(ISDAO $unDAO){
    	$this->tipoOperacion = ISAdministradorPersistencia::OPERACION_CREACION;
        $unDAO->setAdministradorPersistencia($this);

        $unDAO->limpiarSentencias();
        $unDAO->crear();

        $unosResultados = $this->ejecutarSentencia($unDAO->getSentenciasSQL());
        //$unDAO->procesarPosterior();
        return $unosResultados;
    }
    
    /**
     * Ejecuta una actualizacion(UPDATE) unitaria en la base de datos, la operacion que se ejecuta
	 * depende del DAO que se le pase como parametro
     *
     * @param ISDAO $unDAO
     * @return unknown
     */
    public function actualizar(ISDAO $unDAO){
        $this->tipoOperacion = ISAdministradorPersistencia::OPERACION_ACTUALIZACION;
        $unDAO->setAdministradorPersistencia($this);

        $unDAO->limpiarSentencias();
        $unDAO->actualizar();

        $unosResultados = $this->ejecutarSentencia($unDAO->getSentenciasSQL());
        return $unosResultados;
    }
    
    public function iniciarTransaccion(){
    	$this->conexionDB->beginTransaction();
    }
    
	public function commit(){
    	$this->conexionDB->commit();
    }
    public function rollBack(){
    	$this->conexionDB->rollBack();
    }
    
    /**
     * Metodo auxiliar que es empleado por los 4 anteriores para realizar la ejecucion
     * concreta de una o varias consultas SQL, en el caso de tratarse de varias consultas
     * estas se agrupan dentro de una transaccion.
     * 
     *
     * @param array $unasSentenciasSQL
     * @return unknown
     */
    public function ejecutarSentencia(array $unasSentenciasSQL){
       
    	if ($unasSentenciasSQL != null && count($unasSentenciasSQL) > 0) {
			
    		$results = array();
			
            switch ($this->tipoOperacion) {
            
            //retorno un arreglo que contiene arreglos, cad sub arreglo corresponde a un registro
           	case ISAdministradorPersistencia::OPERACION_CONSULTA:
           		try {
                	$unaConsultaSQL = (String) $unasSentenciasSQL[0];
                	$sentencia = $this->conexionDB->prepare($unaConsultaSQL);
                	$sentencia->execute();
                	return $sentencia->fetchAll(PDO::FETCH_ASSOC);
           		}catch (PDOException $e) {
   					throw($e);
			   	}
            case ISAdministradorPersistencia::OPERACION_CREACION:
            case ISAdministradorPersistencia::OPERACION_ELIMINACION:
            case ISAdministradorPersistencia::OPERACION_ACTUALIZACION:
				
               try {	
               		foreach ($unasSentenciasSQL as $sentenciaSQL){
               			$sentencia = $this->conexionDB->prepare($sentenciaSQL);
                		$sentencia->execute();
                		$results[0] += $sentencia->rowCount();
                		if($this->conexionDB->getAttribute(PDO::ATTR_DRIVER_NAME) == 'mysql' && $this->tipoOperacion == ISAdministradorPersistencia::OPERACION_CREACION){
                			$this->ultimoID = $this->conexionDB->lastInsertId();
                		}
               		}
               		return $results;
               }catch (PDOException $e) {
   					throw($e);
			   }
			break;
            default:
            	
            try {	
               		foreach ($unasSentenciasSQL as $sentenciaSQL){
               			$sentenciaSQL = $this->conexionDB->prepare($sentenciaSQL);
                		$sentenciaSQL->execute();
               		}
               }catch (PDOException $e) {
   					throw($e);
			   }
            break;
            }
        }
        return $results;
    }
    
    /**
     * Metodo auxiliar que puede ser llamado por el metodo "crear"
     * para obtener el valor de la PK del registro recien insertado
     *
     * @param $unNombreSecuencia
     * @return unknown
     */
    /*public function getSecuencia($unNombreSecuencia){
    	
    	if ($this->ultimoID == null ||  $this->ultimoID == 0 || $this->ultimoID == '0'){

    		if ($unNombreSecuencia != null){

        		$sql = "SELECT SCOPE_IDENTITY() AS SECUENCIA";
				$unosResultados = $this->conexionDB->query($sql);
				foreach ($unosResultados as $row) {
					$this->ultimoID = $row['SECUENCIA'];
 	   	    		return $this->ultimoID;
				}
			}
			else {
	        	$this->ultimoID = $this->conexionDB->lastInsertId();
		    	return $this->ultimoID;
    	
        	}
        }
        else {
        	return $this->ultimoID;
        }
    }*/
	
    /**
     * Metodo auxiliar que puede ser llamado por el metodo "crear"
     * para obtener el valor de la PK del registro recien insertado
     *
     * @param $unNombreSecuencia
     * @return unknown
     */
    public function getSecuencia($unNombreSecuencia){

        $sql = "SELECT ". $unNombreSecuencia .
               ".NEXTVAL as PK FROM DUAL";

        $unosResultados = $this->conexionDB->query($sql);
		foreach ($unosResultados as $row) {
			return $row['PK'];
		}
    }

    /**
     * Metodo que inicia una sesion con la base de datos
     *
     */
    private function abrirSesion(){
    	
        if ($this->conexionDB == null){
        	$this->conexionDB = new PDO($this->sdn, $this->usuario, $this->password);
        }
    }

    public function getNombreDataSource() {
        return "SBIDataSource";
    }
    
    public function getColumnas(){
    	return $this->columnas;
    }
    
    public function getConexionDB(){
    	return $this->conexionDB;
    }

    /**
     * Metodo que termina una sesion con la base de datos
     * 
     */
    public function cerrarSesion(){
        if ($this->conexionDB != null) {
            $this->conexionDB = null;
        }
    }
    
    /**
     * En el destructor nuevamente se destruye la sesion con la base de datos
     * en caso que se haya olvidado o no se haya ejecutado el metodo "cerrarSesion"
     * La idea administrar los recursos de computo de la mejor manera aumentado el performance.
     *
     */
    public function __destruct(){
    	if ($this->conexionDB != null) {
            $this->conexionDB = null;
        }
    }
}

?>